<?php

namespace D;

/**
 * Projectile2D
 * 
 * Simulates a projectile launched from the origin (0, 0) with a given initial
 * velocity, resistance, and universal accelerative force (e.g. gravity).
 *
 * @author Jonah Dahlquist <jonah[at]nucleussystems[dot]com>
 */
class Projectile2D
{
	/**
	 * Keeps the inital velocity passed through constructor
	 *
	 * @var Point $initialVelocity
	 */
	protected $initialVelocity = null;
	
	/**
	 * Holds the simulations current velocity
	 *
	 * @var Point $velocity
	 */
	protected $velocity = null;
	
	/**
	 * Holds the projectile's current x & y coordinates
	 *
	 * @var Point $position
	 */
	protected $position = null;
	
	/**
	 * Contains the universal force in x & y direction.  For example,
	 * Jb_Point(0, -1) would accelerate the projectile downward at one foot per
	 * second per second.
	 *
	 * @var Point $gravity
	 */
	protected $gravity = null;
	
	/**
	 * Contains the resistance constant.  This constant is multiplied by the
	 * speed during simulation per-frame to calculate new speed.
	 *
	 * @var double $resistance
	 */
	protected $resistance = null;
	
	/**
	 * Creates a new Projectile2D.
	 *
	 * @param Point $initalVelocity Initial velocity of projectile
	 * @param Point $gravity Universal force
	 * @param double $resistance Resistance (optional, default none)
	 */
	public function  __construct(Point2D $initialVelocity, Point2D $gravity, $resistance = 1)
	{
		$this->initialVelocity = $initialVelocity;
		$this->reset();
		$this->gravity = $gravity;
		$this->resistance = doubleval($resistance);
	}
	
	/**
	 * Runs the simulation a given number of frames
	 *
	 * @param int $frames
	 * @return void
	 */
	public function run($frames)
	{
		while ($frames > 0) {
			$frames--;
			$this->affectVelocityWithGravity();
			$this->affectVelocityWithResistance();
			$this->affectPositionWithVelocity();
		}
	}
	
	/**
	 * Resets the simulation to the origin and initial velocity
	 *
	 * @return void
	 */
	public function reset()
	{
		$this->position = new Point2D();
		$this->velocity = clone $this->initialVelocity;
	}
	
	/**
	 * Gets the projectile's current velocity
	 *
	 * @return Point
	 */
	public function getVelocity()
	{
		return clone $this->velocity;
	}
	
	/**
	 * Gets the projectile's current position
	 *
	 * @return Point
	 */
	public function getPosition()
	{
		return clone $this->position;
	}
	
	/**
	 * Modifies the velocity based on the universal force
	 *
	 * @return void
	 */
	protected function affectVelocityWithGravity()
	{
		$this->velocity->x += $this->gravity->x;
		$this->velocity->y += $this->gravity->y;
	}
	
	/**
	 * Modifies the velocity based on the resistance constant
	 *
	 * @return void
	 */
	protected function affectVelocityWithResistance()
	{
		$this->velocity->x *= $this->resistance;
		$this->velocity->y *= $this->resistance;
	}
	
	/**
	 * Modifies the position based on the velocity
	 *
	 * @return void
	 */
	protected function affectPositionWithVelocity()
	{
		$this->position->x += $this->velocity->x;
		$this->position->y += $this->velocity->y;
	}
}

